package org.cos.generation.util.symbol;

import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;

import org.cos.generation.util.predict.PredictSet;
import org.cos.generation.util.predict.UnionPredictSet;
import org.cos.generation.util.symbol.SymbolElem.SymbolKind;



/**
 * The Symbol Table.
 * @author ZXD
 */
public class SymbolTable {
	
	@SuppressWarnings("serial")
	class SymbolElemStack extends Stack<SymbolElem> {
	}

    /** Store the every scope corresponding possible Set to a Symbol. */
    private Hashtable<String, SymbolElemStack> possTable =
            new Hashtable<String, SymbolElemStack>();

    /** Store the new Symbol Lists of every scope.*/
    private Stack<List<String>> symbolStack = new Stack<List<String>>();

    /**
     * Clone the all symbols' possible set of current scope.
     * @return 
     */
    public SymbolList cloneCurrent() {
        SymbolList symbolPossList = new SymbolList();
        for (Entry<String, SymbolElemStack> symbolEntry : possTable.entrySet()) {
            String symbol = symbolEntry.getKey();
            PredictSet possSet = symbolEntry.getValue().peek().getPoss();
            symbolPossList.add(new SymbolListElem(symbol, possSet));
        }
        return symbolPossList;
    }

    /**
     * Invoke when enter a scope, create new space of stack to record new symbols.
     */
    public void enterScope() {
    	// 新建一个当前符号表空间并压入当前域符号栈
        symbolStack.push(new LinkedList<String>());
    }

    /**
     * Invoke when exit a scope, recover the symbol table by pop the new symbols.
     */
    public void exitScope() {
    	// 从当前符号栈中获得当前域的新符号列表
        List<String> newSymbolList = symbolStack.pop();
        // 删除这些符号当前的预测集合
        for (String symbol : newSymbolList) {
            removeSymbol(symbol);
        }
    }

    /**
     *
     * @param symName
     */
    public void insertSymbol(String symName, SymbolKind symKind) {
        if (!containsSymbol(symName)) {
        // 如果符号表中没有同名符号
        	
        	// 为该符号新建预测集合栈空间
            possTable.put(symName, new SymbolElemStack());
        }
        
        possTable.get(symName).push(new SymbolElem(symKind));
        symbolStack.peek().add(symName);
    }


    /**
     * Pop the current possible set of a given symbol name.
     * @param symName
     */
    private void removeSymbol(String symName) {
        if (containsSymbol(symName)) {
            possTable.get(symName).pop();
            if (possTable.get(symName).empty()) {
                possTable.remove(symName);
            }
        }
    }


    /**
     * Return the current possible set for a given symbol name.
     * @param symName
     * @return
     */
    public PredictSet getSymbolPoss(String symName) {
        if (containsSymbol(symName)) {
            return possTable.get(symName).peek().getPoss();
        }
        return null;
    }

    public SymbolKind getSymbolKind(String symName) {
        if (containsSymbol(symName)) {
            return possTable.get(symName).peek().getKind();
        }
        return null;
    }

    /**
     * Add a possible set into current space of a given symbol through a Set.
     * @param symName
     * @param possSet
     * @return
     */
    public boolean putPoss(String symName, PredictSet possSet) {
        if (containsSymbol(symName)) {
            if (possSet != null) {
            	possTable.get(symName).peek().setPoss(possSet);
            }
            return true;
        }
        return false;
    }

    public boolean addPoss(String symName, PredictSet possSet) {
        if (containsSymbol(symName)) {
            if (possSet != null) {
            	UnionPredictSet newPossSet = new UnionPredictSet();
            	newPossSet.addChild(possTable.get(symName).peek().getPoss());
            	newPossSet.addChild(possSet);
                possTable.get(symName).peek().setPoss(newPossSet);
            }
            return true;
        }
        return false;
    }

    /**
     * Return whether the symbol table contains a given symbol.
     * @param symName The given symbol name.
     * @return TRUE if the symbol table contains the given symbol.
     */
    public boolean containsSymbol(String symName) {
        return possTable.containsKey(symName);
    }
    
    public Set<String> getSymbols() {
        return possTable.keySet();
    }

    public boolean union(SymbolList symbolList) {
        if (symbolList != null) {
            for (SymbolListElem symbolElem : symbolList) {
                String symbol = symbolElem.getSymbol();
                PredictSet possSet = symbolElem.getPoss();
                if (containsSymbol(symbol)) {
                    addPoss(symbol, possSet);
                }
            }
            return true;
        }
        return false;
    }

    public boolean modify(SymbolList symbolList) {
        if (symbolList != null) {
            for (SymbolListElem symbolElem : symbolList) {
                String symbol = symbolElem.getSymbol();
                PredictSet possSet = symbolElem.getPoss();
                if (!containsSymbol(symbol)) {
                    putPoss(symbol, possSet);
                }
            }           
            return true;
        }
        return false;
    }

}


